#ifndef __JK_ARRAY_H_
#define __JK_ARRAY_H_

#include "jk_base.h"
#include "jk_algorithm.h"
#include "jk_iterator.h"
#include "jk_container.h"

JK_LIB_NAMESPACE_BEGIN

template<typename T, int size>
class Array;

template<typename T>
class DynamicArray;


template<typename T, int size>
class Array
    : public Container<T, Array<T, size>>
{
public:
    typedef Container<T, Array<T, size>>    super_type;
    typedef typename super_type::value_type          value_type;
    typedef typename super_type::reference           reference;
    typedef typename super_type::const_reference     const_reference;
    typedef typename super_type::pointer             pointer;
    typedef typename super_type::const_pointer       const_pointer;

public:
    Array()
    {
    }

    template<typename U>
    Array(const Array<U, size>& arry)
    {
        for (int i = 0; i < Size; i++)
        {
            data_[i] = arry[i];
        }
    }

    ~Array()
    {
    }

public:
    int GetNumOfElements() const
    {
        return size;
    }

    void Reverse()
    {
        int i = 0;
        int j = size - 1;
        value_type temp;
        while (i < j)
            JkAlgorithm::Exchange(data_[i], data_[j]);
    }

    iterator Begin()
    {
        iterator it(this);
        return it;
    }

    iterator End()
    {
        iterator it(this);
        return it;
    }

    pointer GetData()
    {
        return data_;
    }

public:
    reference operator[](int index)
    {
        return GetElementAt(index);
    }

private:
    reference GetElementAt(int index)
    {
        assert(abs(index) < size);
        if (index >= 0)
            return data_[index];
        else
            return data_[size + index];
    }

private:
    value_type  data_[size];
    int         num_of_elements_;
};


template <typename T>
class DynamicArray
{
public:
    typedef T           value_type;
    typedef T&          reference;
    typedef const T&    const_reference;
    typedef T*          pointer;
    typedef const T*    const_pointer;
    typedef int         size_type;

public:
    DynamicArray()
        : data_(NULL)
        , num_of_elements_(0)
    {

    }

    template <typename U>
    DynamicArray(const DynamicArray<U>& another)
    {
        data_ = another.data_;
        num_of_elements_ = another.GetNumOfElements();
        max_num_of_elements_ = another.GetMaxNumOfElements();
        return (*this);
    }

    ~DynamicArray()
    {
        Destory();
    }

public:
    int GetNumOfElements() const
    {
        return num_of_elements_;
    }

    int GetMaxNumOfElements() const
    {
        return max_num_of_elements_;
    }

    bool Reserve(int size)
    {
        if (size <= num_of_elements_)
            return false;
        
        T* temp = new T[size];
        for (int i = 0; i < num_of_elements_; i++)
            temp[i] = data_[i];

        Destory();
        data_ = temp;
        num_of_elements_ = size;
        return true;
    }

    void Reverse()
    {
        int i = 0;
        int j = size - 1;
        value_type temp;
        while (i < j)
            JkAlgorithm::Exchange(data_[i], data_[j]);
    }

    void Exchange(const DynamicArray<T>& another)
    {
        value_type* temp = another.data_;
        size_type temp_size = another.size_;
        another.data_ = data_;
        another.size_ = num_of_elements_;
        data_ = temp;
        num_of_elements_ = temp_size;
    }

    bool IsEmpty()
    {
        return (num_of_elements_ == 0);
    }

    void Clear()
    {
        num_of_elements_ = 0;
    }

    void Add(const T& element)
    {
        if (num_of_elements_ >= max_num_of_elements_)
            Expand(num_of_elements_ + 1);

        data_[num_of_elements_++] = element;
    }

public:
    reference operator[](int index) const
    {
        return GetElementAt(index);
    }

    template <typename U>
    DynamicArray<T>& operator=(const DynamicArray<U>& another)
    {
        if (&another == this)
            return another;

        Destory();
        data_ = another.data_;
        num_of_elements_ = another.size_;
        return (*this);
    }

private:
    reference GetElementAt(int index) const
    {
        assert(abs(index) < num_of_elements_);
        if (index >= 0)
            return data_[index];
        else
            return data_[num_of_elements_ + index];
    }

    void Destory()
    {
        if (data_ != NULL)
        {
            delete []data_;
            data_ = NULL;
        }
        num_of_elements_ = 0;
    }

    void Expand(int num_of_need)
    {
        if (num_of_need < max_num_of_elements_)
            return;

        do 
        {
            max_num_of_elements_ += 16;
        } while (max_num_of_elements_ < num_of_need);

        T* temp = new T[max_num_of_elements_];
        memcpy_s(temp, max_num_of_elements_ * sizeof(T), data_, num_of_elements_ * sizeof(T));

        if (data_ != NULL)
            delete []data_;

        data_ = temp;
    }

private:
    T* data_;
    int num_of_elements_;
    int max_num_of_elements_;
};

JK_LIB_NAMESPACE_END


#endif //!__JK_ARRAY_H_
